#ifndef SYMBOL_H
#define SYMBOL_H

#include "parser.h"
#include "libblogc/list.h"

typedef enum symbol_type
{ 
  SYMBOL_B_TYPE,                             /* a BLOG type */
  SYMBOL_B_FUNCTION,                         /* a BLOG function */
  SYMBOL_B_OBJECT,                           /* a BLOG object */
} symbol_type;

typedef enum symbol_fn_cat
{
  SYMBOL_FN_CAT_RANDOM,
  SYMBOL_FN_CAT_NONRANDOM,
  SYMBOL_FN_CAT_ORIGIN,
  SYMBOL_FN_CAT_NUMBER /* the derived var created for each number stmt */
} symbol_fn_cat;

typedef struct symbolc_type
{
  list_t      objlist;              /* list of objects of this type */
  list_t      origfns;                  /* list of origin functions */
  list_t      attrs;                   /* attributes (or unary fns) */
  list_t      incoming_fns; /* functions returning objects of this type */
  int         genstruct; /* should a structure be generated for this type */
  char *      gendeclname;             /* name used in declarations */
  int         has_guarobjs;      /* does it have guaranteed objects */
  int         has_obsobjs;         /* does it have observed objects */
  struct symbol * null_numfn; /* symbol for num stmt without origin fn */
  int         infinite;        /* type with infinitely many objects */

  list_t      childfns; /* functions which refer to all objects of the type */

  int         repr_pointer;
  int         repr_int;
  int         repr_double;
} symbolc_type;

#define symbol_has_fixed_objects(typ) \
((typ)->t.has_guarobjs || (typ)->t.has_obsobjs)

#define symbol_has_unknown_num_objects(typ) \
(!symbol_has_fixed_objects(typ) && !(typ)->t.infinite)

typedef struct symbolc_object
{
  struct symbol * objtype;      /* type of the object */
  int             objnum;       /* the number assigned to object */
  
  /* observations on attributes of the object */
  list_t  obs_attr_vals;
  
} symbolc_object;

typedef struct symbolc_function
{
  symbol_fn_cat  category;  /* category */
  struct symbol *rettype;   /* return type */
  void *  argtype_list;     /* list of argument types */
  void *  argname_list;     /* list of argument names */
  struct node *  tree;      /* the syntax tree */
  struct node *  zary_obs;  /* observation of zero-ary function */

  list_t childfns;                           /* child functions */
  list_t childattrs;         /* child attributes of the same object */
  list_t child_in_attrs;  /* child attributes of incoming functions */
  list_t block_child;       /* child attributes to be block sampled */

  struct symbol * numfn; /* symbol for corresponding number statement */
  int has_obs; /* there was some observation involving this function */

  struct cpdtype_t
  {
    unsigned int uncond_uniform:1;
  } cpdtype;
} symbolc_function;

typedef struct symbol
{
  char *      name;
  symbol_type symtype;
  int         builtin;                /* true / false */
  char *      genname;                       /* generated name */
  
  symbolc_type t;
  symbolc_object o;
  symbolc_function f;
  
} symbol;


void symbol_table_alloc(void);
void symbol_table_free(void);
void symbol_print(symbol * sym);
void symbol_table_print(void);

/* start a hash table iterator over the symbol table's hashtable */
void * symbol_table_itr_start(void);

/* returns 1 if the type already exists 
   name - pointer to name
   (on success, name belongs to the symbol table; on failure, it belongs to the
   caller)
 */
int symbol_table_new_type(char * name);

/* returns 1 if the type already exists 
   (on success, ptrs belongs to the symbol table; on failure, it belongs to the
   caller)
 */
int symbol_table_new_function_decl(int category, symbol * ret_type,
                                   char * name, void * argtype_list);

/* returns 1 if the type already exists 
   (on success, ptrs belongs to the symbol table; on failure, it belongs to the
   caller)
   isguar -> is it a guaranteed object (if not then its observed)
 */
int symbol_table_new_object(symbol * obj_type, char * obj_name);

/*
  finds a symbol in the table or NULL
  name - pointer to name
*/
symbol * symbol_table_find(char * name);

/*
  gets the BLOG type  or exits with an error
*/
symbol * symbol_table_gettype(char * typename);

/*
  gets the BLOG object or exits with an error
*/
symbol * symbol_table_getobj(char * objname);

/*
  gets the BLOG function or exits with an error
*/
symbol * symbol_table_getfn(char * fnname);

#endif /* SYMBOL_H */
